﻿namespace NTLib.Core.Memory
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    using System.Runtime.Serialization;

    /// <summary>
    /// Define an helper to implement correctly the IDispose pattern
    /// </summary>
    /// <seealso cref="NTLib.Core.Memory.IDisposableExt" />
    [DataContract]
    public class Disposable : IDisposableExt
    {
        #region Fields

        /// <summary>
        /// Define if the pattern must suppress the finalizer
        /// </summary>
        private readonly bool _needUnmanagedDispose;

        /// <summary>
        /// To detect redundant calls
        /// </summary>
        private bool _disposedValue = false;

        #endregion

        #region Dtor

        /// <summary>
        /// Initializes a new instance of the <see cref="Disposable"/> class.
        /// </summary>
        /// <param name="needunmanageddispose">if set to <c>true</c> the <see cref="Disposable"/> class will apply the GC.SuppressFinalizer process.</param>
        public Disposable(bool needunmanageddispose = false)
        {
            this._needUnmanagedDispose = needunmanageddispose;
        }

        /// <summary>
        /// Finalizes an instance of the <see cref="Disposable"/> class.
        /// </summary>
        ~Disposable()
        {
            Dispose(false);
        }


        #endregion

        #region Properties

        /// <summary>
        /// Gets a value indicating whether this instance is disposed.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
        /// </value>
        [IgnoreDataMember]
        public bool IsDisposed
        {
            get { return this._disposedValue; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Dispose(true);

            if (this._needUnmanagedDispose)
                GC.SuppressFinalize(this);
        }


        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        /// <exception cref="System.AggregateException">Group all the excptions occured during the dispose process.</exception>
        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposedValue)
            {
                List<Exception> exs = null;
                if (disposing)
                {
                    try
                    {
                        ManagedDispose();
                    }
                    catch (Exception ex)
                    {
                        if (exs == null)
                            exs = new List<Exception>();
                        exs.Add(ex);
                    }
                }

                try
                {
                    UnManagedDispose();
                }
                catch (Exception ex)
                {
                    if (exs == null)
                        exs = new List<Exception>();
                    exs.Add(ex);
                }

                try
                {
                    DisposeEveryTime();
                }
                catch (Exception ex)
                {
                    if (exs == null)
                        exs = new List<Exception>();
                    exs.Add(ex);
                }

                if (exs != null)
                    throw new AggregateException(exs);

                this._disposedValue = true;
            }
        }

        /// <summary>
        /// Dispose methods called whenever is the dipose origin.
        /// </summary>
        protected virtual void DisposeEveryTime()
        {
        }

        /// <summary>
        /// Dispose the managed objects.
        /// </summary>
        protected virtual void ManagedDispose()
        {
        }

        /// <summary>
        /// Dispose the UnManaged memory.
        /// </summary>
        protected virtual void UnManagedDispose()
        {
        }

        /// <summary>
        /// Checks if the instance is disposed.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        protected void CheckDisposed()
        {
            if (this.IsDisposed)
                throw new ObjectDisposedException(this.GetType().Name);
        }

        #endregion
    }
}
